package com.example.common.snowflake;

import jakarta.annotation.PostConstruct;

public class SnowflakeIdGenerator {

    // 时间起始标记点（2020-01-01）
    private final static long EPOCH = 1577808000000L;

    // 机器ID和数据中心ID占用的位数
    private final static long MACHINE_BIT = 5;   // 机器标识位数
    private final static long DATA_CENTER_BIT = 5; // 数据中心标识位数

    // 序列号占用的位数（12位，支持每毫秒生成4096个ID）
    private final static long SEQUENCE_BIT = 12;

    // 最大值计算（位运算优化）
    private final static long MAX_MACHINE_ID = ~(-1L << MACHINE_BIT);
    private final static long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_BIT);
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    // 各部分的位移
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;

    private long sequence = 0L; // 序列号
    private long lastTimestamp = -1L; // 上次生成时间
    private long machineId; // 机器ID
    private long dataCenterId; // 数据中心ID

    public SnowflakeIdGenerator(long machineId, long dataCenterId) {
        this.machineId = machineId;
        this.dataCenterId = dataCenterId;
    }

    @PostConstruct
    public void init() {
        // 校验机器ID和数据中心ID的合法性
        if (machineId > MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("机器ID需在范围 [0, " + MAX_MACHINE_ID + "] 内");
        }
        if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
            throw new IllegalArgumentException("数据中心ID需在范围 [0, " + MAX_DATA_CENTER_ID + "] 内");
        }
    }

    /**
     * 生成下一个唯一ID（线程安全）
     */
    public synchronized long nextId() {
        long currentTimestamp = timeGen();

        if (currentTimestamp < lastTimestamp) {
            // 时钟回拨时抛出异常（简单处理，实际可能需要更复杂的策略）
            throw new RuntimeException("时钟回拨，拒绝生成ID");
        }

        if (currentTimestamp == lastTimestamp) {
            // 同一毫秒内序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0) {
                // 当前毫秒序列号用尽，等待下一毫秒
                currentTimestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L; // 新的一毫秒重置序列号
        }

        lastTimestamp = currentTimestamp;

        // 组合各部分生成ID
        return ((currentTimestamp - EPOCH) << TIMESTAMP_LEFT)
                | (dataCenterId << DATA_CENTER_LEFT)
                | (machineId << MACHINE_LEFT)
                | sequence;
    }

    /**
     * 阻塞到下一毫秒
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 获取当前时间戳（毫秒）
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }
}